// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <chrono>
#include <iomanip>
#include <iostream>
#include <map>
#include <random>
#include <string>
#include <vector>

#include <turbo/files/filesystem.h>
#include <gtest/gtest.h>

namespace tbir::test {

    enum class TempOpt {
        none, change_path
    };

    class TemporaryDirectory {
    public:
        TemporaryDirectory(TempOpt opt = TempOpt::none) {
            auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
            std::mt19937 gen(static_cast<unsigned int>(seed) ^
                             static_cast<unsigned int>(reinterpret_cast<ptrdiff_t>(&opt)));
            std::uniform_int_distribution<int> random_dist(0, 35);  // define the range
            do {
                std::string filename;
                filename.reserve(16);
                filename.append("test_");
                for (int i = 0; i < 8; ++i) {
                    filename.push_back("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[random_dist(gen)]);
                }
                tmp_path_ = turbo::filesystem::canonical(turbo::filesystem::temp_directory_path()) / filename;
            } while (turbo::filesystem::exists(tmp_path_));

            turbo::filesystem::create_directories(tmp_path_);
            if (opt == TempOpt::change_path) {
                orig_dir_ = turbo::filesystem::current_path();
                turbo::filesystem::current_path(tmp_path_);
            }
        }

        ~TemporaryDirectory() {
            // restore current_path
            if (!orig_dir_.empty()) {
                turbo::filesystem::current_path(orig_dir_);
            }
            // clear tmp path
            turbo::filesystem::remove_all(tmp_path_);
        }

        const turbo::filesystem::path &path() const {
            return tmp_path_;
        }

    private:
        turbo::filesystem::path tmp_path_;
        turbo::filesystem::path orig_dir_;
    };

    static void generateFile(const turbo::filesystem::path &pathname, int withSize = -1) {
        turbo::filesystem::ofstream outfile(pathname);
        if (withSize < 0) {
            outfile << "Hello world!" << std::endl << std::flush;
        } else {
            outfile << std::string(size_t(withSize), '*') << std::flush;
        }
    }

    template<typename TP>
    std::time_t to_time_t(TP tp) {
        // Based on trick from: Nico Josuttis, C++17 - The Complete Guide
        std::chrono::system_clock::duration dt =
                std::chrono::duration_cast<std::chrono::system_clock::duration>(tp - TP::clock::now());
        return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now() + dt);
    }

    static std::string perm_to_str(turbo::filesystem::perms prms) {
        std::string result;
        result.reserve(9);
        for (int i = 0; i < 9; ++i) {
            result = ((static_cast<int>(prms) & (1 << i)) ? "xwrxwrxwr"[i] : '-') + result;
        }

        return result;
    }
}  // namespace tbir::test
